from datetime import datetime, timedelta
import json
import sys
import os
import traceback  # 用于打印异常栈信息
from typing import List
from PyQt6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtCore import QUrl
import pandas as pd
from qfluentwidgets import (
    NavigationInterface,
    NavigationItemPosition,
    setTheme,
    Theme,
)

# 检查外部依赖是否导入成功
try:
    from JsonReplace import JsonPlaceholderReplacer  # 需确保本地存在该模块
    from tushare_das import getDayStock  # 需确保本地存在该模块
except ImportError:
    AntLogger.info("缺少必要的模块 `JsonReplace` 或 `tushare_das`，请检查后重试。")
    sys.exit(1)


class StockChartWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Stock Chart with PyQt-Fluent-Widgets and Echarts")
        self.resize(1200, 800)

        # 设置主题
        setTheme(Theme.LIGHT)

        # 创建导航界面
        navigation_interface = NavigationInterface(self)
        navigation_interface.addItem(
            routeKey="home",
            icon="home",
            text="Home",
            position=NavigationItemPosition.TOP,
        )
        navigation_interface.addItem(
            routeKey="about",
            icon="info",
            text="About",
            position=NavigationItemPosition.BOTTOM,
        )

        # 创建布局和控件
        layout = QVBoxLayout()
        self.web_view = QWebEngineView()

        # 添加控件到布局
        layout.addWidget(self.web_view)

        # 创建中央控件并设置布局
        central_widget = QWidget()
        central_widget.setLayout(layout)
        self.setCentralWidget(central_widget)

        # 加载HTML文件
        self.do_load_chat()

    def do_load_chat(self):
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            chat_html_file = "stock_day.html"
            chat_path = os.path.join(current_dir, chat_html_file)
            if not os.path.exists(chat_path):
                raise FileNotFoundError(f"{chat_html_file} 文件不存在")

            self.web_view.load(QUrl.fromLocalFile(chat_path))
            self.web_view.loadFinished.connect(self.do_load_data)
        except Exception as e:
            AntLogger.info(f"加载HTML失败: {e}")

    def do_load_data(self):
        try:
            current_dir = os.path.dirname(os.path.abspath(__file__))
            chat_json_file = "stock_day.json"
            local_file_path = os.path.join(current_dir, chat_json_file)

            if not os.path.exists(local_file_path):
                raise FileNotFoundError(f"{chat_json_file} 文件不存在")

            with open(local_file_path, "r", encoding="utf-8") as file:
                json_data = json.load(file)

            # 调用函数方法进行解析
            chat_data = self.data_exchange(json_data)
            js_code = f"setData({chat_data});"
            AntLogger.info(f"最终写入参数: {js_code}")
            self.web_view.page().runJavaScript(js_code, self.echat_callback)
        except Exception as e:
            AntLogger.info(f"加载数据失败: {e}")

    def echat_callback(self, result):
        AntLogger.info(f"JavaScript Result: {result}")

    def data_exchange(self, json_data: dict) -> str:
        try:
            today = datetime.today()
            end_time = today.strftime("%Y%m%d")
            start_time = (today - timedelta(days=180)).strftime("%Y%m%d")
            stock_data = getDayStock("600519.SH", start_time, end_time)

            # 检查数据是否为空
            if stock_data is None or stock_data.empty:
                raise ValueError("股票数据为空，无法解析")

            dates_data = self.get_dates(stock_data)
            kline_data = self.get_kline_data(stock_data)
            point_data = self.get_point_data(stock_data)
            mark_data = self.get_markline_data(stock_data)

            replacer = JsonPlaceholderReplacer(json_data)
            replacer.replace_placeholder("__DATAS__", dates_data)
            replacer.replace_placeholder("__STOCK_NAME__", "写死一个骗骗老板")
            replacer.replace_placeholder("__STOCK_DATA__", kline_data)
            replacer.replace_placeholder("__POINT_DATA__", point_data)
            replacer.replace_placeholder("__MARK_DATA__", mark_data)

            return replacer.get_modified_json()
        except Exception as e:
            # 打印异常栈信息
            AntLogger.info(f"数据处理失败: {e}")
            AntLogger.info("异常栈信息如下:")
            AntLogger.infotraceback.format_exc())
            return "{}"

    def get_kline_data(self, stock_data: pd.DataFrame) -> List[List[float]]:
        """
        从股票数据中提取 K 线图数据。
        :param stock_data: Tushare 返回的 DataFrame，包含股票每日交易数据
        :return: K 线图数据 [[open, close, low, high], ...]
        """
        if stock_data.empty:
            AntLogger.info("股票数据为空，无法生成 K 线数据")
            return []

        required_columns = ["open", "close", "low", "high"]
        missing_columns = [
            col for col in required_columns if col not in stock_data.columns
        ]
        if missing_columns:
            raise ValueError(f"缺少以下必要列，无法生成 K 线数据: {missing_columns}")

        # 使用 DataFrame 的 values 提取多列数据为二维列表
        kline_data = stock_data[["open", "close", "low", "high"]].values.tolist()
        AntLogger.info(f"K 线数据生成成功，共 {len(kline_data)} 条记录")
        return kline_data

    def get_dates(self, stock_data: pd.DataFrame) -> List[str]:
        """
        从股票数据中提取并格式化交易日期。
        :param stock_data: Tushare 返回的 DataFrame，包含股票每日交易数据
        :return: 格式化后的日期列表 ['2023-01-01', '2023-01-02', ...]
        """
        if stock_data.empty:
            AntLogger.info("股票数据为空，无法生成日期列表")
            return []

        if "trade_date" not in stock_data.columns:
            raise ValueError("数据中缺少 'trade_date' 列，无法生成日期列表")

        # 使用 apply 格式化日期列
        dates = (
            stock_data["trade_date"]
            .apply(lambda x: datetime.strptime(str(x), "%Y%m%d").strftime("%Y-%m-%d"))
            .tolist()
        )
        AntLogger.info(f"日期列表生成成功，共 {len(dates)} 条记录")
        return dates

    def get_point_data(self, stock_data) -> List[dict]:
        """
        生成包含最高值和最低值的点数据。

        参数:
            stock_data (DataFrame): 包含股票数据的 DataFrame。

        返回:
            List[dict]: 包含最高值和最低值点数据的列表。
        """
        point_data = [
            {"name": "最高值", "type": "max", "valueDim": "highest"},
            {"name": "最低值", "type": "min", "valueDim": "lowest"},
        ]
        return point_data

    def get_markline_data(self, stock_data: pd.DataFrame) -> dict:
        """
        生成辅助线 (markLine) 数据，用于标注收盘价的平均值。
        :param stock_data: Tushare 返回的 DataFrame，包含股票每日交易数据
        :return: 包含辅助线数据的字典
        """
        if stock_data.empty:
            AntLogger.info("股票数据为空，无法生成平均线数据")
            return {"data": []}

        if "close" not in stock_data.columns:
            raise ValueError("数据中缺少 'close' 列，无法生成平均线数据")

        # 计算收盘价平均值
        average_price = stock_data["close"].mean()
        AntLogger.info(f"收盘价平均值计算成功: {average_price:.2f}")

        # 构造辅助线数据
        return {
            "data": [
                {
                    "yAxis": average_price,
                    "name": "平均值",
                    "lineStyle": {
                        "color": "#FF0000",  # 虚线颜色
                        "type": "dashed",  # 虚线样式
                        "width": 2,  # 线条宽度
                    },
                    "label": {
                        "formatter": f"平均值: {average_price:.2f}",  # 显示均值
                        "position": "end",  # 文本位置
                        "color": "#FF0000",  # 文本颜色
                    },
                }
            ]
        }


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = StockChartWindow()
    window.show()
    sys.exit(app.exec())
